package com.SwipeMenuLayout.jw;

import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.OvershootInterpolator;
import android.view.ViewParent;
import android.os.Parcelable;
import android.os.Bundle;

/**
 * @作者 : JW (qq3247681675)

 * 精简版 SwipeMenuLayout
 * 1. 第一个 child 为内容，其余为菜单
 * 2. 默认左滑展开（右侧菜单），可设置右滑展开（左侧菜单）
 * 3. 边界停住、反方向实时跟手
 */
public class SwipeMenuLayout extends ViewGroup {

    private static final String TAG = "SwipeMenuLayout";

    /* ---------------------- 构造 ---------------------- */
    public SwipeMenuLayout(Context context) { this(context, null); }
    public SwipeMenuLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); }
    public SwipeMenuLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        mMaxVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();
    }

    /* ---------------------- 基础尺寸 ---------------------- */
    private int mScaledTouchSlop;
    private int mMaxVelocity;
    private int mMenuWidth = 0;
    private View mContentView;

    /* ---------------------- 手势 ---------------------- */
    private float mDownX = 0;          // DOWN 位置
    private float mLastX = 0;          // 上一 MOVE 位置
    private int mPointerId;
    private VelocityTracker mVelocity;

    /* ---------------------- 开关 ---------------------- */
    private boolean isEnableLeftMenu;   // true 右滑出现左侧菜单
    private boolean isOpenChoke = true; // 阻塞模式
    private static SwipeMenuLayout mCacheView; // 全局单例

    /* ---------------------- 测量 / 布局 ---------------------- */
    @Override
    protected void onMeasure(int wMS, int hMS) {
        int width = MeasureSpec.getSize(wMS);
        int height = 0;
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == GONE) continue;
            measureChild(child, wMS, hMS);
            height = Math.max(height, child.getMeasuredHeight());
            if (i == 0) {
                mContentView = child;
                mMenuWidth = 0;
            } else {
                mMenuWidth += child.getMeasuredWidth();
            }
        }
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int top = getPaddingTop();
        int bottom = top + getMeasuredHeight();

        /* 1. 内容区域 */
        View content = getChildAt(0);
        if (content != null) {
            content.layout(0, top, getMeasuredWidth(), bottom);
        }

        /* 2. 菜单区域：横向排列 */
        int menuLeft;
        int childCount = getChildCount();
        if (!isEnableLeftMenu) {           // 菜单在右侧
            menuLeft = getMeasuredWidth();
            for (int i = 1; i < childCount; i++) {
                View menu = getChildAt(i);
                if (menu == null || menu.getVisibility() == GONE) continue;
                int w = menu.getMeasuredWidth();
                menu.layout(menuLeft, top, menuLeft + w, bottom);
                menuLeft += w;
            }
        } else {                           // 菜单在左侧
            menuLeft = -mMenuWidth;
            for (int i = 1; i < childCount; i++) {
                View menu = getChildAt(i);
                if (menu == null || menu.getVisibility() == GONE) continue;
                int w = menu.getMeasuredWidth();
                menu.layout(menuLeft, top, menuLeft + w, bottom);
                menuLeft += w;
            }
        }
    }

    /* ---------------------- 事件 ---------------------- */
    //阻塞拦截的一个控制变量
    private boolean chokeIntercept = false;
    
    /* 主要是为了实现点击菜单外自动收起，和判断点击菜单是否应该自动收起 */
    private float mTouchDownX = 0;          // 记录 DOWN 的 X
    private float mTouchDownY = 0;          // 记录 DOWN 的 Y
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mTouchDownX = ev.getX();
                mTouchDownY = ev.getY();
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                // 1. 菜单已完全展开才需要处理
                if (Math.abs(getScrollX()) == mMenuWidth) {
                    // 2. 判断是否为点击：位移足够小
                    float dx = Math.abs(ev.getX() - mTouchDownX);
                    float dy = Math.abs(ev.getY() - mTouchDownY);
                    if (dx < mScaledTouchSlop && dy < mScaledTouchSlop) {
                        // 3. 点击在菜单区域？
                        boolean hitMenu = (isEnableLeftMenu && ev.getX() < mMenuWidth)
                               || (!isEnableLeftMenu && ev.getX() > getMeasuredWidth() - mMenuWidth);
                        if (hitMenu) {
                            /* ===== 新增：计算到底是第几个按钮被点 ===== */
                            int x = (int) (ev.getX() + getScrollX());   // 关键：加上 scrollX
                            int index = 0;     // 菜单按钮索引
                            View targetView = null;

                            // 逐个比对每个菜单按钮的边界
                            for (int i = 1; i < getChildCount(); i++) {
                                View menu = getChildAt(i);
                                if (menu == null || menu.getVisibility() == GONE) continue;

                                int left = menu.getLeft();
                                int right = menu.getRight();
                                if (x >= left && x <= right) {
                                    targetView = menu;
                                    index = i - 1;   // 0 开始
                                    break;
                                }
                            }
                            
                            if (mClickListener != null && targetView != null) {
                                mClickListener.menuClick(targetView, index);
                            }
                            /* ========================================== */

                            if (mClickMenuToClose) {
                                close();
                            }
                            return true;  // 消费掉
                        } else {
                            close();               // 点击内容关闭
                            return true;
                        }
                    }
                }
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mPointerId = ev.getPointerId(0);
                mDownX = ev.getRawX();
                mLastX = mDownX;
                getParent().requestDisallowInterceptTouchEvent(false);
                chokeIntercept = false;
                if (mCacheView != null) {
                    if (mCacheView != this) {
                        mCacheView.close();
                        chokeIntercept = isOpenChoke;
                    }
                    //屏蔽父类的事件,只要有一个侧滑菜单处于打开状态， 就不给外层布局上下滑动了
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (mVelocity == null) mVelocity = VelocityTracker.obtain();
        mVelocity.addMovement(ev);

        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mPointerId = ev.getPointerId(0);
                mDownX = ev.getRawX();
                mLastX = mDownX;
                // 抢焦点
                if (mCacheView != null) {
                    if (mCacheView != this) mCacheView.close();
                }
                return true;

            case MotionEvent.ACTION_MOVE:
                //有阻塞
                if (chokeIntercept) {
                    break;
                }
            
                int idx = ev.findPointerIndex(mPointerId);
                if (idx < 0) break;
                float curX = ev.getX(idx);
                float dx = mLastX - curX;

                int target = (int) (getScrollX() + dx);
                
                // 边界裁剪
                if (!isEnableLeftMenu) {          // 右边菜单
                    target = Math.max(0, Math.min(target, mMenuWidth));
                } else {                          // 左边菜单
                    target = Math.max(-mMenuWidth, Math.min(target, 0));
                }
                
                getParent().requestDisallowInterceptTouchEvent(true);
                scrollTo(target, 0);
                
                mLastX = curX;
                return true;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mVelocity.computeCurrentVelocity(1000, mMaxVelocity);
                float vx = mVelocity.getXVelocity(mPointerId);
                mVelocity.recycle();
                mVelocity = null;

                int scrollX = getScrollX();
                boolean shouldOpen;
                
                if (Math.abs(vx) > 1000) {           // 快速滑动
                    shouldOpen = vx < 0 ^ isEnableLeftMenu;
                } else {                             // 临界值
                    shouldOpen = Math.abs(scrollX) > mMenuWidth / 2;
                }
                
                if (!chokeIntercept) {
                if (shouldOpen) open();
                else close();
                }
                return true;
        }
        return super.onTouchEvent(ev);
    }

    /* ---------------------- 动画 ---------------------- */
    private ValueAnimator mAnimator;
    private void cancelAnim() { if (mAnimator != null) mAnimator.cancel(); }

    public void open() {
        cancelAnim();
        mCacheView = this;
        int end = isEnableLeftMenu ? -mMenuWidth : mMenuWidth;
        mAnimator = ValueAnimator.ofInt(getScrollX(), end)
            .setDuration(mAnimDuration);
        mAnimator.setInterpolator(new OvershootInterpolator(mEnableOverScroll ? 2f : 0f));
        mAnimator.addUpdateListener(a -> scrollTo((int) a.getAnimatedValue(), 0));
        mAnimator.addListener(new android.animation.Animator.AnimatorListener() {
            @Override public void onAnimationStart(android.animation.Animator animation) {
                if (mStateListener != null) mStateListener.menuIsOpen(SwipeMenuLayout.this, true);
            }
            @Override public void onAnimationEnd(android.animation.Animator animation) {}
            @Override public void onAnimationCancel(android.animation.Animator animation) {}
            @Override public void onAnimationRepeat(android.animation.Animator animation) {}
        });
        mAnimator.start();
    }

    public void close() {
        cancelAnim();
        if (mCacheView == this) mCacheView = null;
        mAnimator = ValueAnimator.ofInt(getScrollX(), 0)
            .setDuration(mAnimDuration);
        mAnimator.setInterpolator(new AccelerateInterpolator());
        mAnimator.addUpdateListener(a -> scrollTo((int) a.getAnimatedValue(), 0));
        mAnimator.addListener(new android.animation.Animator.AnimatorListener() {
            @Override public void onAnimationStart(android.animation.Animator animation) {
                if (mStateListener != null) mStateListener.menuIsOpen(SwipeMenuLayout.this, false);
            }
            @Override public void onAnimationEnd(android.animation.Animator animation) {}
            @Override public void onAnimationCancel(android.animation.Animator animation) {}
            @Override public void onAnimationRepeat(android.animation.Animator animation) {}
        });
        mAnimator.start();
    }
    
    //快速关闭 没有动画时间
    public void quickClose() {
        if (getScrollX() != 0) {
            cancelAnim();
            scrollTo(0, 0);
            mCacheView = null;
        }
    }

    //快速打开 没有动画时间
    public void quickOpen() {
        if (getScrollX() == 0) {
            cancelAnim();
            int x = isEnableLeftMenu ? -mMenuWidth : mMenuWidth;
            scrollTo(x, 0);
            mCacheView = null;
        }
    }
    
    /* ---------------------- 公开 API ---------------------- */
    public SwipeMenuLayout setEnableLeftMenu(boolean enable) {
        isEnableLeftMenu = enable;
        return this;
    }

    public SwipeMenuLayout setOpenChoke(boolean choke) {
        isOpenChoke = choke;
        return this;
    }

    /* ---------------------- 回收 ---------------------- */
    @Override
    protected void onDetachedFromWindow() {
        //避免多次调用
        if (getScrollX() != 0) {
            quickClose();
            mCacheView = null;
        }
        super.onDetachedFromWindow();
    }
    
    
    /* ---------------------- 动画时长 ---------------------- */
    private long mAnimDuration = 250;     // 默认 250 ms

    /**
     * 设置展开/收起动画时长（毫秒）
     */
    public SwipeMenuLayout setAnimDuration(long duration) {
        if (duration >= 0) {
            mAnimDuration = duration;
        }
        return this;
    }

    /* ---------------------- 展开状态 ---------------------- */
    /**
     * 当前菜单是否处于展开状态
     */
    public boolean isExpandMenu() {
        return Math.abs(getScrollX()) >= mMenuWidth;
    }

    /* ---------------------- 收展状态监听 ---------------------- */
    public interface SwipeMenuStateListener {
        /** 菜单收展回调 */
        void menuIsOpen(SwipeMenuLayout layout, boolean isOpen);
    }

    private SwipeMenuStateListener mStateListener;

    /**
     * 设置菜单收起/展开状态监听
     */
    public SwipeMenuLayout setSwipeMenuStateListener(SwipeMenuStateListener listener) {
        mStateListener = listener;
        return this;
    }
    
    
    /* ---------------------- 点击菜单自动收起 ---------------------- */
    private boolean mClickMenuToClose = true;   // 默认允许点击菜单后收起

    /**
     * 设置点击菜单区域是否自动收起菜单
     * @param enable true-点击菜单后自动收起；false-不处理
     */
    public SwipeMenuLayout setClickMenuAndClose(boolean enable) {
        mClickMenuToClose = enable;
        return this;
    }
    
    
    /**
     * 设置是否允许快速滑动时越界回弹
     * @param enable true-允许快速滑动的越界回弹；false-禁止快速滑动时视觉上的越界
     */
    private boolean mEnableOverScroll = false;   // true 允许快速滑动的视觉越界回弹；false 禁止
    public SwipeMenuLayout setEnableOverScroll(boolean enable) {
        mEnableOverScroll = enable;
        return this;
    }
    
    /* ---------------------- 菜单点击监听 ---------------------- */
    public interface SwipeMenuClickListener {
        /**
         * 某个菜单组件被点击
         * @param clickView 被点击的那个菜单组件 View
         * @param clickIndex 该菜单组件View 在所有菜单中的索引（0-based）
         */
        void menuClick(View clickView, int clickIndex);
    }
    private SwipeMenuClickListener mClickListener;
    public SwipeMenuLayout setSwipeMenuClickListener(SwipeMenuClickListener l) {
        mClickListener = l;
        return this;
    }

    
    
    /* 重建时修正相关代码 */
    @Override
    protected Parcelable onSaveInstanceState() {
        Bundle bundle = new Bundle();
        bundle.putParcelable("super", super.onSaveInstanceState());
        bundle.putInt("scrollX", getScrollX());
        bundle.putInt("menuW", mMenuWidth);
        return bundle;
    }
    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle) {
            Bundle bundle = (Bundle) state;
            int oldScroll = bundle.getInt("scrollX", 0);
            int oldMenuW  = bundle.getInt("menuW", 0);

            super.onRestoreInstanceState(bundle.getParcelable("super"));

            // 如果菜单宽度还没量出来，先强制回到关闭状态
            if (oldMenuW <= 0) {
                scrollTo(0, 0);
            } else {
                // 保证恢复后的偏移值不越界
                int fix = isEnableLeftMenu
                        ? Math.max(-oldMenuW, Math.min(oldScroll, 0))
                        : Math.max(0, Math.min(oldScroll, oldMenuW));
                scrollTo(fix, 0);
            }
        } else {
            super.onRestoreInstanceState(state);
        }
    }

}